
#include "cpppython.hpp"

using namespace dynamic::python;


var & wrap_var::get_var(PyObject * self)
{
	return ((wrap_var*)self)->m_value;
}


void wrap_var::destructor(PyObject * self)
{
	get_var(self).~var();
}


PyObject * wrap_var::add(PyObject * self, PyObject * op)
{
	try
	{
		return var_to_py( get_var(self) + py_to_var(op) );
	}
	catch(var & ex)
	{
		set_py_error(ex);
		return 0;
	}
}


PyObject * wrap_var::call(PyObject * self, PyObject * args, PyObject * kw)
{
	try
	{
		// Error if kw is supplied
		// Error if args is 
		switch( PyTuple_Size(args) )
		{
		case 0:
			return var_to_py( get_var(self)() );
		case 1:
			return var_to_py( get_var(self)( py_to_var(PyTuple_GetItem(args, 0) ) ) );
		case 2:
			return var_to_py( get_var(self)( 
				py_to_var(PyTuple_GetItem(args, 0)), 
				py_to_var(PyTuple_GetItem(args, 1)) ) );
			// !! etc
		default:
			// Throw to many args !!
			;
		}
		return 0;
	}
	catch(var & ex)
	{
		set_py_error(ex);
		return 0;
	}
}


PyObject * wrap_var::get_attr(PyObject * self, char * s)
{
	try
	{
		return var_to_py( get_var(self)[(const char*)s] );
	}
	catch(var & ex)
	{
		set_py_error(ex);
		return 0;
	}
}


PyObject * wrap_var::get_attro(PyObject * self, PyObject * attr)
{
	const char * s = PyString_AsString(attr);
	// !! Exceptions
	return var_to_py( get_var(self)[(const char*)s] );
}


Py_ssize_t wrap_var::mp_length(PyObject * self)
{
	return get_var(self).size();
}


PyObject * wrap_var::mp_subscript(PyObject * self, PyObject * member)
{
	try
	{
		if(PyString_Check(member))
		{
			return var_to_py( get_var(self)[PyString_AS_STRING(member)]);
		}

		if(PyInt_Check(member))
		{
			return var_to_py( get_var(self)[PyInt_AS_LONG(member)] );
		}

		return var_to_py( get_var(self)[py_to_var(member)] );
	}
	catch( var & ex )
	{
		set_py_error(ex);
		return 0;
	}
}


int wrap_var::mp_ass_subscript(PyObject * self, PyObject *member, PyObject *value)
{
	try
	{
		get_var(self)[py_to_var(member)] = py_to_var(value);
		return 1;
	}
	catch( var & ex )
	{
		set_py_error(ex);
		return 0;
	}
}


namespace
{
	PyNumberMethods var_number_methods = 
	{
		wrap_var::add
	};

	PyMappingMethods var_mapping_methods = 
	{
		wrap_var::mp_length,
		wrap_var::mp_subscript,
		wrap_var::mp_ass_subscript
	};
}

namespace dynamic
{
namespace python
{
	PyTypeObject var_type = 
	{ 
		PyObject_HEAD_INIT(&PyType_Type)
		0,
		"dynamic.var",	 // !!
		sizeof(wrap_var), 
		0,
		wrap_var::destructor,
		0, // printfn,
		wrap_var::get_attr, // getattrfn,
		0, // setattrfn,
		0, // cmpfn,
		0, // reprfn,
		&var_number_methods, // NumberMethods
		0, // SequenceMethods
		&var_mapping_methods, // Mapping methods
		0, // hash_fn
		wrap_var::call, // call,
		0, // strfn
		wrap_var::get_attro, // getattro
		0, // setattro,
		0, // as buffer
		0, // flags,
		"A C++Script var",
		0, // traverse ??
		0, // tp_clear
		0, // rich compare
		0, // weak lists
		0, // get iterator
		0, // iterator next
		0, // methods
		0, // members
		0, // getset
		0, // base
		0, // dict
		0, // desc_get
		0, // desc_set
		0, // dict offset
		0, // init_proc
		0, // alloc fn
		0, // new fn
		0, // free
		0, // is_gc,
		0, // bases
		0, 
	};
}
}


PyObject * dynamic::python::var_to_py(const var & v)
{
	if(v.impl().comparison_index() == cmp_python)
	{
		PyObject * result = (PyObject*)v.impl().as_ptr();
		Py_INCREF(result);
		return result;
	}

	wrap_var * vo = (wrap_var*)_PyObject_New(&var_type);
	v.impl().copy_to(&vo->m_value.m_variant);
	return vo;
}


void dynamic::python::set_py_error(const var & ex)
{
	PyErr_SetNone(var_to_py(ex));
}
